import os
import re
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import threading
from datetime import datetime
import tkinter.font as tkfont
import ctypes

class FileSearchTool:
    def __init__(self, root):
        """初始化文件搜索工具"""
        self.root = root
        self.root.title("高级文件搜索工具")
        self.root.geometry("1000x650")
        self.root.minsize(900, 600)
        
        # 创建线程锁
        self.ui_lock = threading.Lock()
        
        # 创建样式，用于ttk控件的字体设置
        self.style = ttk.Style()
        
        # 显示初始界面
        self.show_splash_screen()
        
        # 启动后台初始化线程
        self.init_thread = threading.Thread(target=self.initialize_application, daemon=True)
        self.init_thread.start()
        
        # 定期检查初始化是否完成
        self.check_initialization()
    
    def show_splash_screen(self):
        """显示启动界面"""
        self.splash_frame = ttk.Frame(self.root, padding="20")
        self.splash_frame.pack(fill=tk.BOTH, expand=True)
        
        # 应用标题
        title_label = ttk.Label(
            self.splash_frame, 
            text="高级文件搜索工具", 
            font=("SimHei", 24, "bold")
        )
        title_label.pack(pady=40)
        
        # 加载动画
        self.progress_var = tk.DoubleVar()
        progress_bar = ttk.Progressbar(
            self.splash_frame, 
            orient=tk.HORIZONTAL, 
            length=300, 
            mode='indeterminate',
            variable=self.progress_var
        )
        progress_bar.pack(pady=20)
        progress_bar.start()
        
        # 状态文本
        self.status_var = tk.StringVar(value="正在初始化...")
        status_label = ttk.Label(self.splash_frame, textvariable=self.status_var)
        status_label.pack(pady=10)
    
    def initialize_application(self):
        """在后台线程中执行耗时的初始化操作"""
        try:
            # 设置中文字体
            self.setup_fonts()
            
            # 更新状态
            self.status_var.set("正在获取可用驱动器...")
            
            # 搜索范围选项
            self.search_scope = tk.StringVar(value="drives")  # 默认使用驱动器搜索
            
            # 获取可用驱动器（在后台线程中执行）
            self.drives = self.get_available_drives()
            
            # 标记初始化完成
            self.initialized = True
        except Exception as e:
            self.initialized = False
            self.error_message = str(e)
    
    def check_initialization(self):
        """检查初始化是否完成"""
        if hasattr(self, 'initialized') and self.initialized:
            # 初始化完成，移除启动界面并显示主界面
            self.splash_frame.destroy()
            self.create_main_interface()
        elif hasattr(self, 'error_message'):
            # 初始化出错，显示错误信息
            self.splash_frame.destroy()
            messagebox.showerror("初始化错误", f"应用初始化失败: {self.error_message}")
        else:
            # 继续等待
            self.root.after(100, self.check_initialization)
    
    def create_main_interface(self):
        """创建主界面"""
        # 创建主框架
        self.main_frame = ttk.Frame(self.root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 顶部参数和进度平行布局框架
        self.top_frame = ttk.Frame(self.main_frame)
        self.top_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 变量初始化
        self.search_thread = None
        self.stop_event = threading.Event()
        self.search_in_progress = False
        self.total_files = 0
        self.searched_files = 0
        self.results = []
        self.search_start_time = None
        self.last_update_time = None
        self.last_speed = 0
        
        # 历史搜索记录
        self.search_history = []
        
        # 初始化按钮引用
        self.start_button = None
        self.stop_button = None
        self.clear_button = None
        
        # 新增：严格模式变量（默认不启用）
        self.strict_mode = tk.BooleanVar(value=False)
        
        # 创建搜索参数区域（左侧）
        self.create_search_params_frame()
        
        # 创建增强的进度显示区域（右侧，与参数框平行）
        self.create_enhanced_progress_display()
        
        # 创建结果显示区域（降低高度）
        self.create_results_frame()
        
        # 创建状态栏
        self.create_status_bar()
    
    def setup_fonts(self):
        """配置字体，确保中文正常显示"""
        font_families = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "Microsoft YaHei", "Arial"]
        
        # 设置默认字体
        default_font = tkfont.nametofont("TkDefaultFont")
        text_font = tkfont.nametofont("TkTextFont")
        
        # 只检查系统中第一个可用的字体
        for family in font_families:
            if family in tkfont.families():
                default_font.configure(family=family, size=10)
                text_font.configure(family=family, size=10)
                
                # 为ttk控件设置字体样式
                self.style.configure("TCheckbutton", font=(family, 9))
                self.style.configure("TLabel", font=(family, 10))
                self.style.configure("TButton", font=(family, 10))
                self.style.configure("TEntry", font=(family, 9))
                break
    
    def create_search_params_frame(self):
        """创建搜索参数区域（左侧），添加内框结构（核心修改：降低整体高度）"""
        # 左侧参数框架，占据约60%宽度，限制最大高度
        self.params_frame = ttk.LabelFrame(self.top_frame, text="搜索参数", padding="8")
        self.params_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        # 使用画布和滚动条实现参数区域的滚动功能
        self.params_canvas = tk.Canvas(self.params_frame, highlightthickness=0)
        self.params_scrollbar = ttk.Scrollbar(self.params_frame, orient="vertical", command=self.params_canvas.yview)
        self.params_scrollable_frame = ttk.Frame(self.params_canvas)
        
        # 配置画布和滚动条
        self.params_scrollable_frame.bind(
            "<Configure>",
            lambda e: self.params_canvas.configure(
                scrollregion=self.params_canvas.bbox("all"),
                height=min(e.height, 450)  # 最大高度限制为220像素
            )
        )
        
        self.params_canvas.create_window((0, 0), window=self.params_scrollable_frame, anchor="nw")
        self.params_canvas.configure(yscrollcommand=self.params_scrollbar.set)
        
        # 放置画布和滚动条
        self.params_canvas.pack(side="left", fill="both", expand=True)
        self.params_scrollbar.pack(side="right", fill="y")
        
        # ---------------------------
        # 搜索模式内框
        # ---------------------------
        mode_inner_frame = ttk.LabelFrame(self.params_scrollable_frame, text="搜索模式", padding="6")
        mode_inner_frame.grid(row=0, column=0, columnspan=3, sticky=tk.W+tk.E, pady=(0, 4))
        
        ttk.Label(mode_inner_frame, text="搜索类型:").grid(row=0, column=0, sticky=tk.W, pady=3)
        self.search_mode = tk.StringVar(value="file_name")
        # 新增：绑定搜索模式切换事件，控制严格模式复选框的启用/禁用
        ttk.Radiobutton(
            mode_inner_frame, 
            text="文件名", 
            variable=self.search_mode, 
            value="file_name",
            command=self.toggle_strict_mode  # 切换为文件名模式时启用严格模式
        ).grid(row=0, column=1, sticky=tk.W, pady=3)
        ttk.Radiobutton(
            mode_inner_frame, 
            text="文件内容", 
            variable=self.search_mode, 
            value="file_content",
            command=self.toggle_strict_mode  # 切换为内容模式时禁用严格模式
        ).grid(row=0, column=2, sticky=tk.W, pady=3)
        
        # ---------------------------
        # 搜索文本内框
        # ---------------------------
        text_inner_frame = ttk.LabelFrame(self.params_scrollable_frame, text="搜索内容", padding="6")
        text_inner_frame.grid(row=1, column=0, columnspan=3, sticky=tk.W+tk.E, pady=(0, 4))
        
        ttk.Label(text_inner_frame, text="搜索文本:").grid(row=0, column=0, sticky=tk.W, pady=3)
        self.search_text = tk.StringVar()
        ttk.Entry(text_inner_frame, textvariable=self.search_text, width=30).grid(
            row=0, column=1, columnspan=2, sticky=tk.W+tk.E, pady=3, padx=(0, 5)
        )
        
        # 新增：严格模式复选框（默认禁用，仅在文件名模式启用）
        self.strict_checkbox = ttk.Checkbutton(
            text_inner_frame, 
            text="严格模式（完全匹配文件名）", 
            variable=self.strict_mode,
            state=tk.NORMAL  # 初始状态：因默认搜索模式是文件名，故启用
        )
        self.strict_checkbox.grid(row=1, column=0, columnspan=3, sticky=tk.W, pady=2, padx=5)
        
        # ---------------------------
        # 搜索范围内框
        # ---------------------------
        scope_inner_frame = ttk.LabelFrame(self.params_scrollable_frame, text="搜索范围", padding="6")
        scope_inner_frame.grid(row=2, column=0, columnspan=3, sticky=tk.W+tk.E, pady=(0, 4))
        
        ttk.Label(scope_inner_frame, text="范围选择:").grid(row=0, column=0, sticky=tk.W, pady=3)
        
        # 搜索范围选项
        scope_radio_frame = ttk.Frame(scope_inner_frame)
        scope_radio_frame.grid(row=0, column=1, sticky=tk.W, pady=3)
        
        self.select_all_drives_var = tk.BooleanVar(value=True)
        ttk.Radiobutton(
            scope_radio_frame, 
            text="所有驱动器", 
            variable=self.search_scope, 
            value="drives", 
            command=lambda: [self.update_search_scope(), self.select_all_drives(self.select_all_drives_var.get())]
        ).grid(row=0, column=0, sticky=tk.W, padx=3)
        
        ttk.Radiobutton(
            scope_radio_frame, 
            text="特定文件夹", 
            variable=self.search_scope, 
            value="folder", 
            command=self.update_search_scope
        ).grid(row=0, column=1, sticky=tk.W, padx=3)
        
        # 全选/取消全选按钮（使用样式设置字体，不再使用font参数）
        self.select_all_checkbox = ttk.Checkbutton(
            scope_inner_frame, 
            text="全选/取消全选", 
            variable=self.select_all_drives_var,
            command=lambda: self.select_all_drives(self.select_all_drives_var.get())
        )
        self.select_all_checkbox.grid(row=0, column=2, sticky=tk.E, pady=3)
        
        # 驱动器选择
        self.drives_frame = ttk.Frame(scope_inner_frame)
        self.drives_frame.grid(row=1, column=0, columnspan=3, sticky=tk.W+tk.E, pady=3)
        
        self.drive_vars = {}
        col = 0
        max_cols = 6  # 增加列数减少垂直高度
        for drive in self.drives:
            var = tk.BooleanVar(value=False)
            if drive == "C:\\":  # 默认选中C盘
                var.set(True)
            self.drive_vars[drive] = var
            ttk.Checkbutton(self.drives_frame, text=drive, variable=var).grid(
                row=col // max_cols, column=col % max_cols, padx=2, pady=1
            )
            col += 1
        
        # 特定文件夹选择
        self.folder_frame = ttk.Frame(scope_inner_frame)
        self.folder_frame.grid(row=1, column=0, columnspan=3, sticky=tk.W+tk.E, pady=3)
        
        self.selected_folder = tk.StringVar()
        ttk.Entry(self.folder_frame, textvariable=self.selected_folder, width=38).grid(
            row=0, column=0, sticky=tk.W+tk.E, pady=3, padx=(0, 3)
        )
        ttk.Button(self.folder_frame, text="浏览...", command=self.browse_folder, width=8).grid(
            row=0, column=1, sticky=tk.W, pady=3
        )
        
        # 初始时隐藏文件夹选择框
        self.folder_frame.grid_remove()
        
        # ---------------------------
        # 高级选项内框
        # ---------------------------
        advanced_inner_frame = ttk.LabelFrame(self.params_scrollable_frame, text="高级选项", padding="6")
        advanced_inner_frame.grid(row=3, column=0, columnspan=3, sticky=tk.W+tk.E, pady=(0, 4))
        
        # 第一行选项
        options_row1 = ttk.Frame(advanced_inner_frame)
        options_row1.pack(fill=tk.X, pady=2)
        
        # 包含子目录
        self.include_subdirs = tk.BooleanVar(value=True)
        ttk.Checkbutton(options_row1, text="包含子目录", variable=self.include_subdirs).pack(side=tk.LEFT, padx=8)
        
        # 区分大小写
        self.case_sensitive = tk.BooleanVar(value=False)
        ttk.Checkbutton(options_row1, text="区分大小写", variable=self.case_sensitive).pack(side=tk.LEFT, padx=8)
        
        # 使用正则表达式
        self.use_regex = tk.BooleanVar(value=False)
        ttk.Checkbutton(options_row1, text="使用正则表达式", variable=self.use_regex).pack(side=tk.LEFT, padx=8)
        
        # 第二行选项
        options_row2 = ttk.Frame(advanced_inner_frame)
        options_row2.pack(fill=tk.X, pady=2)
        
        # 文件类型过滤
        ttk.Label(options_row2, text="文件类型过滤 (逗号分隔):").pack(side=tk.LEFT, padx=(0, 3))
        self.file_types = tk.StringVar()
        ttk.Entry(options_row2, textvariable=self.file_types, width=18).pack(side=tk.LEFT)
        
        # ---------------------------
        # 搜索历史内框
        # ---------------------------
        history_inner_frame = ttk.LabelFrame(self.params_scrollable_frame, text="搜索历史", padding="6")
        history_inner_frame.grid(row=4, column=0, columnspan=3, sticky=tk.W+tk.E, pady=(0, 4))
        
        # 缩小历史列表框高度
        self.history_listbox = tk.Listbox(history_inner_frame, width=48, height=1, font=("SimHei", 9))
        self.history_listbox.pack(side=tk.LEFT, fill=tk.X, expand=True, pady=2)
        
        # 水平滚动条
        scrollbar = ttk.Scrollbar(history_inner_frame, orient=tk.HORIZONTAL, command=self.history_listbox.xview)
        scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        self.history_listbox.configure(xscrollcommand=scrollbar.set)
        
        # ---------------------------
        # 操作按钮内框
        # ---------------------------
        buttons_inner_frame = ttk.LabelFrame(self.params_scrollable_frame, text="操作", padding="6")
        buttons_inner_frame.grid(row=5, column=0, columnspan=3, sticky=tk.W+tk.E, pady=(0, 2))
        
        # 搜索按钮
        buttons_frame = ttk.Frame(buttons_inner_frame)
        buttons_frame.pack(fill=tk.X, pady=2)
        
        # 保存按钮引用
        self.start_button = ttk.Button(buttons_frame, text="开始搜索", command=self.start_search, width=10)
        self.start_button.pack(side=tk.LEFT, padx=3)
        
        self.stop_button = ttk.Button(buttons_frame, text="停止搜索", command=self.stop_search, state=tk.DISABLED, width=10)
        self.stop_button.pack(side=tk.LEFT, padx=3)
        
        self.clear_button = ttk.Button(buttons_frame, text="清空结果", command=self.clear_results, width=10)
        self.clear_button.pack(side=tk.LEFT, padx=3)
        
        # 初始时选中所有驱动器
        self.select_all_drives(True)
    
    # 新增：根据搜索模式切换严格模式复选框的启用状态
    def toggle_strict_mode(self):
        """切换搜索模式时，控制严格模式复选框的启用/禁用"""
        if self.search_mode.get() == "file_name":
            self.strict_checkbox.config(state=tk.NORMAL)  # 文件名模式：启用严格模式
        else:
            self.strict_checkbox.config(state=tk.DISABLED)  # 内容模式：禁用严格模式
            self.strict_mode.set(False)  # 同时取消勾选
    
    def select_all_drives(self, select=True):
        """全选或取消全选所有驱动器"""
        with self.ui_lock:
            for drive, var in self.drive_vars.items():
                var.set(select)
            self.select_all_drives_var.set(select)
    
    def update_search_scope(self):
        """更新搜索范围显示"""
        if self.search_scope.get() == "drives":
            self.drives_frame.grid()
            self.folder_frame.grid_remove()
        else:
            self.drives_frame.grid_remove()
            self.folder_frame.grid()
    
    def browse_folder(self):
        """浏览并选择文件夹"""
        directory = filedialog.askdirectory()
        if directory:
            self.selected_folder.set(directory)
    
    def create_enhanced_progress_display(self):
        """创建增强的进度显示区域（右侧，与参数框平行）"""
        # 右侧进度框架，占据约40%宽度
        self.progress_frame = ttk.LabelFrame(self.top_frame, text="搜索进度", padding="10")
        self.progress_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
        
        # 主进度条
        self.progress_var = tk.DoubleVar(value=0)
        self.progress_bar = ttk.Progressbar(
            self.progress_frame, 
            orient=tk.HORIZONTAL, 
            length=100, 
            mode='determinate',
            variable=self.progress_var
        )
        self.progress_bar.pack(fill=tk.X, pady=(0, 10))
        
        # 进度详情区域
        details_frame = ttk.Frame(self.progress_frame)
        details_frame.pack(fill=tk.BOTH, expand=True)
        
        # 使用网格布局排列进度信息
        ttk.Label(details_frame, text="总文件数:", font=("SimHei", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=3, padx=5)
        self.total_files_var = tk.StringVar(value="0")
        ttk.Label(details_frame, textvariable=self.total_files_var).grid(row=0, column=1, sticky=tk.W, pady=3)
        
        ttk.Label(details_frame, text="已搜索:", font=("SimHei", 10, "bold")).grid(row=1, column=0, sticky=tk.W, pady=3, padx=5)
        self.searched_files_var = tk.StringVar(value="0 个文件")
        ttk.Label(details_frame, textvariable=self.searched_files_var).grid(row=1, column=1, sticky=tk.W, pady=3)
        
        ttk.Label(details_frame, text="已找到:", font=("SimHei", 10, "bold")).grid(row=2, column=0, sticky=tk.W, pady=3, padx=5)
        self.found_files_var = tk.StringVar(value="0 个结果")
        ttk.Label(details_frame, textvariable=self.found_files_var).grid(row=2, column=1, sticky=tk.W, pady=3)
        
        ttk.Label(details_frame, text="搜索速度:", font=("SimHei", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=3, padx=5)
        self.search_speed_var = tk.StringVar(value="0 文件/秒")
        ttk.Label(details_frame, textvariable=self.search_speed_var).grid(row=3, column=1, sticky=tk.W, pady=3)
        
        ttk.Label(details_frame, text="已用时间:", font=("SimHei", 10, "bold")).grid(row=4, column=0, sticky=tk.W, pady=3, padx=5)
        self.elapsed_time_var = tk.StringVar(value="00:00:00")
        ttk.Label(details_frame, textvariable=self.elapsed_time_var).grid(row=4, column=1, sticky=tk.W, pady=3)
        
        ttk.Label(details_frame, text="剩余时间:", font=("SimHei", 10, "bold")).grid(row=5, column=0, sticky=tk.W, pady=3, padx=5)
        self.estimated_time_var = tk.StringVar(value="计算中...")
        ttk.Label(details_frame, textvariable=self.estimated_time_var).grid(row=5, column=1, sticky=tk.W, pady=3)
        
        # 添加搜索状态显示
        ttk.Label(details_frame, text="搜索状态:", font=("SimHei", 10, "bold")).grid(row=6, column=0, sticky=tk.W, pady=3, padx=5)
        self.search_status_var = tk.StringVar(value="就绪")
        ttk.Label(details_frame, textvariable=self.search_status_var).grid(row=6, column=1, sticky=tk.W, pady=3)
    
    def create_results_frame(self):
        """创建结果显示区域（降低高度）"""
        results_frame = ttk.LabelFrame(self.main_frame, text="搜索结果", padding="10")
        results_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 设置最大高度为窗口的40%
        self.results_frame = results_frame  # 保存引用以便后续更新高度
        
        # 创建表格
        columns = ("path", "size", "modified", "line", "content")
        self.result_tree = ttk.Treeview(results_frame, columns=columns, show="headings")
        self.result_tree.heading("path", text="文件路径")
        self.result_tree.heading("size", text="大小")
        self.result_tree.heading("modified", text="修改日期")
        self.result_tree.heading("line", text="行号")
        self.result_tree.heading("content", text="内容")
        
        self.result_tree.column("path", width=300)
        self.result_tree.column("size", width=80, anchor=tk.CENTER)
        self.result_tree.column("modified", width=120, anchor=tk.CENTER)
        self.result_tree.column("line", width=50, anchor=tk.CENTER)
        self.result_tree.column("content", width=300)
        
        # 添加滚动条
        scroll_y = ttk.Scrollbar(results_frame, orient=tk.VERTICAL, command=self.result_tree.yview)
        scroll_x = ttk.Scrollbar(results_frame, orient=tk.HORIZONTAL, command=self.result_tree.xview)
        self.result_tree.configure(yscroll=scroll_y.set, xscroll=scroll_x.set)
        
        # 使用grid布局管理器而不是pack，以便更好地控制大小
        self.result_tree.grid(row=0, column=0, sticky="nsew")
        scroll_y.grid(row=0, column=1, sticky="ns")
        scroll_x.grid(row=1, column=0, sticky="ew")
        
        # 配置网格权重
        results_frame.grid_rowconfigure(0, weight=1)
        results_frame.grid_columnconfigure(0, weight=1)
        
        # 右键菜单
        self.create_context_menu()
        
        # 双击打开文件
        self.result_tree.bind("<Double-1>", self.open_file)
        
        # 一键清空结果按钮
        clear_frame = ttk.Frame(results_frame)
        clear_frame.grid(row=2, column=0, columnspan=2, sticky="ew", pady=5)
        
        self.clear_all_button = ttk.Button(
            clear_frame, 
            text="一键清空所有结果", 
            command=self.clear_all_results
        )
        self.clear_all_button.pack(side=tk.RIGHT, padx=5)
        
        # 绑定窗口大小变化事件，动态调整结果框高度
        self.root.bind("<Configure>", self.on_window_resize)
        
        # 初始高度设置
        self.update_results_height()

    def on_window_resize(self, event):
        """窗口大小变化时更新结果框高度"""
        if event.widget == self.root:  # 确保是主窗口的变化
            self.update_results_height()

    def update_results_height(self):
        """更新结果框高度"""
        # 获取当前窗口高度
        window_height = self.root.winfo_height()
        
        # 计算结果框的最大高度（窗口高度的40%）
        max_height = int(window_height * 0.4)
        
        # 设置结果框的高度
        self.results_frame.configure(height=max_height)
        
        # 更新布局
        self.results_frame.pack_propagate(False)
    
    def create_context_menu(self):
        """创建右键菜单"""
        self.context_menu = tk.Menu(self.root, tearoff=0)
        self.context_menu.add_command(label="打开文件", command=self.open_file)
        self.context_menu.add_command(label="打开文件位置", command=self.open_file_location)
        self.context_menu.add_command(label="删除文件", command=self.delete_file)
        self.context_menu.add_separator()
        self.context_menu.add_command(label="复制文件路径", command=self.copy_file_path)
        
        # 绑定右键菜单
        self.result_tree.bind("<Button-3>", self.show_context_menu)
    
    def show_context_menu(self, event):
        """显示右键菜单"""
        item = self.result_tree.identify_row(event.y)
        if item:
            self.result_tree.selection_set(item)
            self.context_menu.post(event.x_root, event.y_root)
    
    def create_status_bar(self):
        """创建状态栏"""
        self.status_bar = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def get_available_drives(self):
        """获取系统中所有可用的驱动器"""
        try:
            drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
            drives = []
            for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
                if drive_bitmask & 1:
                    drive = f"{letter}:\\"
                    drive_type = ctypes.cdll.kernel32.GetDriveTypeW(drive)
                    if drive_type in (2, 3, 4):  # 可移动磁盘、本地磁盘、网络驱动器
                        drives.append(drive)
                drive_bitmask >>= 1
            return drives
        except:
            import string
            drives = []
            for letter in string.ascii_uppercase:
                drive = f"{letter}:\\"
                try:
                    os.listdir(drive)
                    drives.append(drive)
                except:
                    continue
            return drives
    
    def start_search(self):
        """开始搜索"""
        search_text = self.search_text.get().strip()
        if not search_text:
            messagebox.showwarning("警告", "请输入搜索文本")
            return
        
        # 获取搜索位置
        search_locations = []
        if self.search_scope.get() == "drives":
            selected_drives = [drive for drive, var in self.drive_vars.items() if var.get()]
            if not selected_drives:
                messagebox.showwarning("警告", "请选择至少一个搜索驱动器")
                return
            search_locations = selected_drives
        else:
            folder_path = self.selected_folder.get().strip()
            if not folder_path or not os.path.exists(folder_path):
                messagebox.showwarning("警告", "请选择有效的搜索文件夹")
                return
            search_locations = [folder_path]
        
        # 清空之前的结果
        self.clear_results()
        
        # 重置进度显示
        self.progress_var.set(0)
        self.total_files_var.set("计算中...")
        self.searched_files_var.set("0 个文件")
        self.found_files_var.set("0 个结果")
        self.search_speed_var.set("0 文件/秒")
        self.elapsed_time_var.set("00:00:00")
        self.estimated_time_var.set("计算中...")
        self.search_status_var.set("准备搜索...")
        
        # 更新状态
        self.search_in_progress = True
        self.status_bar.config(text="准备搜索...")
        self.total_files = 0
        self.searched_files = 0
        self.results = []
        self.search_start_time = datetime.now()
        self.last_update_time = self.search_start_time
        self.last_speed = 0
        
        # 更新按钮状态
        self.update_button_states()
        
        # 重置停止事件
        self.stop_event.clear()
        
        # 保存搜索历史
        self.save_search_history()
        
        # 启动搜索线程
        self.search_thread = threading.Thread(target=self.perform_search, args=(search_locations,), daemon=True)
        self.search_thread.start()
        
        # 启动进度更新定时器
        self.update_progress_timer()
    
    def update_button_states(self):
        """更新按钮状态"""
        if self.start_button is not None:
            self.start_button.config(state=tk.DISABLED)
        if self.stop_button is not None:
            self.stop_button.config(state=tk.NORMAL)
    
    def stop_search(self):
        """停止搜索"""
        if self.search_in_progress:
            self.stop_event.set()
            self.search_status_var.set("正在停止搜索...")
            self.status_bar.config(text="正在停止搜索...")
    
    def clear_results(self):
        """清空搜索结果"""
        with self.ui_lock:
            for item in self.result_tree.get_children():
                self.result_tree.delete(item)
            self.results = []
            self.status_bar.config(text="就绪")
            self.search_status_var.set("就绪")
            
            # 重置按钮状态
            if self.start_button is not None:
                self.start_button.config(state=tk.NORMAL)
            if self.stop_button is not None:
                self.stop_button.config(state=tk.DISABLED)
    
    def clear_all_results(self):
        """一键清空所有结果"""
        if not self.result_tree.get_children():
            messagebox.showinfo("提示", "当前没有搜索结果可清空")
            return
            
        if messagebox.askyesno("确认清空", "确定要清空所有搜索结果吗？"):
            self.clear_results()
            messagebox.showinfo("操作完成", "已清空所有搜索结果")
    
    def save_search_history(self):
        """保存搜索历史"""
        search_text = self.search_text.get().strip()
        search_mode = self.search_mode.get()
        # 新增：保存历史时包含严格模式状态
        strict_tag = "[严格]" if self.strict_mode.get() else ""
        history_item = f"[{search_mode}]{strict_tag} {search_text}"
        self.search_history.append(history_item)
        
        # 更新历史列表
        with self.ui_lock:
            self.history_listbox.delete(0, tk.END)
            for item in self.search_history[-5:]:  # 只显示最近5条
                self.history_listbox.insert(tk.END, item)
    
    def load_search_history(self, event):
        """加载搜索历史"""
        selection = self.history_listbox.curselection()
        if selection:
            history_item = self.history_listbox.get(selection[0])
            
            # 解析历史项（新增严格模式标签解析）
            mode_start = history_item.find('[') + 1
            mode_end = history_item.find(']')
            search_mode = history_item[mode_start:mode_end]
            
            # 检查是否包含严格模式标签
            strict_tag = "[严格]"
            if strict_tag in history_item:
                self.strict_mode.set(True)
                text_start = mode_end + len(strict_tag) + 2
            else:
                self.strict_mode.set(False)
                text_start = mode_end + 2
            
            search_text = history_item[text_start:]
            
            # 更新搜索参数
            self.search_mode.set(search_mode)
            self.search_text.set(search_text)
            # 触发模式切换，确保严格模式复选框状态正确
            self.toggle_strict_mode()
    
    def update_progress_timer(self):
        """定期更新进度信息的定时器"""
        if self.search_in_progress and not self.stop_event.is_set():
            self.update_status()
            # 每500毫秒更新一次进度信息
            self.root.after(500, self.update_progress_timer)
    
    def perform_search(self, search_locations):
        """执行搜索操作"""
        search_text = self.search_text.get()
        search_mode = self.search_mode.get()
        include_subdirs = self.include_subdirs.get()
        case_sensitive = self.case_sensitive.get()
        use_regex = self.use_regex.get()
        file_types = [t.strip() for t in self.file_types.get().split(',') if t.strip()]
        # 新增：获取严格模式状态
        strict_mode = self.strict_mode.get()
        
        # 处理搜索文本（严格模式下无需小写转换，保持原格式）
        if not case_sensitive and not use_regex and not strict_mode:
            search_text = search_text.lower()
        
        # 编译正则表达式（如果需要）
        regex_pattern = None
        if use_regex:
            try:
                flags = 0 if case_sensitive else re.IGNORECASE
                regex_pattern = re.compile(search_text, flags)
            except re.error as e:
                self.root.after(0, lambda: messagebox.showerror("正则表达式错误", f"无效的正则表达式: {str(e)}"))
                self.search_complete()
                return
        
        # 计算要搜索的文件总数
        self.search_status_var.set("正在计算文件总数...")
        self.total_files = self.count_files_to_search(search_locations, include_subdirs, file_types)
        self.root.after(0, lambda: self.total_files_var.set(str(self.total_files)))
        
        # 如果没有文件可搜索，直接完成
        if self.total_files == 0:
            self.root.after(0, lambda: messagebox.showinfo("搜索结果", "没有找到符合条件的文件"))
            self.search_complete()
            return
        
        # 更新状态为搜索中
        self.search_status_var.set("正在搜索...")
        
        # 执行搜索
        for location in search_locations:
            if self.stop_event.is_set():
                break
            
            if os.path.isfile(location):
                # 直接搜索单个文件
                self.search_file(location, search_text, search_mode, case_sensitive, use_regex, regex_pattern, strict_mode)
                self.searched_files += 1
            else:
                # 搜索目录
                self.search_directory(location, search_text, search_mode, case_sensitive, use_regex, regex_pattern, include_subdirs, file_types, strict_mode)
        
        self.search_complete()
    
    def count_files_to_search(self, locations, include_subdirs, file_types):
        """计算要搜索的文件总数"""
        count = 0
        for location in locations:
            if os.path.isfile(location):
                count += 1
            else:
                for root, _, files in os.walk(location):
                    for file in files:
                        # 检查文件类型过滤
                        if file_types:
                            ext = os.path.splitext(file)[1][1:].lower()
                            if ext not in file_types:
                                continue
                        count += 1
                    if not include_subdirs:
                        break
        return count
    
    def search_directory(self, directory, search_text, search_mode, case_sensitive, use_regex, regex_pattern, include_subdirs, file_types, strict_mode):
        """搜索目录中的文件（新增strict_mode参数）"""
        try:
            for root, _, files in os.walk(directory):
                if self.stop_event.is_set():
                    return
                
                for file in files:
                    if self.stop_event.is_set():
                        return
                    
                    # 检查文件类型过滤
                    if file_types:
                        ext = os.path.splitext(file)[1][1:].lower()
                        if ext not in file_types:
                            continue
                    
                    file_path = os.path.join(root, file)
                    self.search_file(file_path, search_text, search_mode, case_sensitive, use_regex, regex_pattern, strict_mode)
                    self.searched_files += 1
                
                if not include_subdirs:
                    break
        except Exception as e:
            pass  # 忽略无法访问的目录
    
    def search_file(self, file_path, search_text, search_mode, case_sensitive, use_regex, regex_pattern, strict_mode):
        """搜索单个文件（核心修改：添加严格模式匹配逻辑）"""
        try:
            if search_mode == "file_name":
                # 搜索文件名
                file_name = os.path.basename(file_path)  # 获取完整文件名（含扩展名，如"test.txt"）
                
                if use_regex:
                    # 正则表达式模式：不受严格模式影响
                    if regex_pattern.search(file_name):
                        self.add_result(file_path, 0, "")
                else:
                    # 普通文本模式：区分严格/非严格
                    if strict_mode:
                        # 严格模式：完全匹配（区分大小写由case_sensitive控制）
                        if case_sensitive:
                            is_match = (file_name == search_text)
                        else:
                            is_match = (file_name.lower() == search_text.lower())
                    else:
                        # 非严格模式：包含匹配
                        compare_text = file_name if case_sensitive else file_name.lower()
                        is_match = (search_text in compare_text)
                    
                    if is_match:
                        self.add_result(file_path, 0, "")
            else:
                # 搜索文件内容（严格模式不影响内容搜索）
                try:
                    with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                        for line_num, line in enumerate(f, 1):
                            if self.stop_event.is_set():
                                break
                            
                            if use_regex:
                                if regex_pattern.search(line):
                                    self.add_result(file_path, line_num, line.strip())
                            else:
                                compare_line = line if case_sensitive else line.lower()
                                if search_text in compare_line:
                                    self.add_result(file_path, line_num, line.strip())
                except UnicodeDecodeError:
                    # 尝试其他编码
                    try:
                        with open(file_path, 'r', encoding='latin-1') as f:
                            for line_num, line in enumerate(f, 1):
                                if self.stop_event.is_set():
                                    break
                                
                                if use_regex:
                                    if regex_pattern.search(line):
                                        self.add_result(file_path, line_num, line.strip())
                                else:
                                    compare_line = line if case_sensitive else line.lower()
                                    if search_text in compare_line:
                                        self.add_result(file_path, line_num, line.strip())
                    except:
                        pass  # 无法打开文件
        except Exception as e:
            pass  # 忽略无法打开的文件
    
    def add_result(self, file_path, line_num, content):
        """添加搜索结果"""
        try:
            # 获取文件大小和修改日期
            file_stats = os.stat(file_path)
            file_size = self.format_size(file_stats.st_size)
            modified_time = datetime.fromtimestamp(file_stats.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
            
            self.root.after(0, lambda: self.result_tree.insert("", tk.END, values=(file_path, file_size, modified_time, line_num, content)))
            with self.ui_lock:
                self.results.append((file_path, line_num, content))
        except:
            # 如果获取文件信息失败，仍添加结果
            self.root.after(0, lambda: self.result_tree.insert("", tk.END, values=(file_path, "未知", "未知", line_num, content)))
            with self.ui_lock:
                self.results.append((file_path, line_num, content))
    
    def format_size(self, size_bytes):
        """格式化文件大小显示"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes/1024:.1f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes/(1024*1024):.1f} MB"
        else:
            return f"{size_bytes/(1024*1024*1024):.1f} GB"
    
    def update_status(self):
        """更新状态栏信息和进度条"""
        if self.total_files > 0 and self.search_in_progress:
            # 计算进度
            progress = min(100.0, (self.searched_files / self.total_files) * 100)
            
            # 计算搜索时间
            current_time = datetime.now()
            elapsed_time = (current_time - self.search_start_time).total_seconds()
            
            # 格式化已用时间为 HH:MM:SS
            elapsed_hours = int(elapsed_time // 3600)
            elapsed_minutes = int((elapsed_time % 3600) // 60)
            elapsed_seconds = int(elapsed_time % 60)
            formatted_elapsed = f"{elapsed_hours:02d}:{elapsed_minutes:02d}:{elapsed_seconds:02d}"
            
            # 计算搜索速度
            if elapsed_time > 0:
                speed = self.searched_files / elapsed_time
            else:
                speed = 0
                
            # 平滑速度计算
            speed = (self.last_speed * 2 + speed) / 3
            self.last_speed = speed
            
            # 计算预计剩余时间
            if speed > 0:
                remaining_files = self.total_files - self.searched_files
                estimated_seconds = remaining_files / speed
                
                if estimated_seconds < 60:
                    estimated_time = f"{estimated_seconds:.1f} 秒"
                elif estimated_seconds < 3600:
                    minutes = int(estimated_seconds // 60)
                    seconds = int(estimated_seconds % 60)
                    estimated_time = f"{minutes} 分 {seconds} 秒"
                else:
                    hours = int(estimated_seconds // 3600)
                    minutes = int((estimated_seconds % 3600) // 60)
                    estimated_time = f"{hours} 小时 {minutes} 分"
            else:
                estimated_time = "计算中..."
            
            # 更新UI
            status_text = f"搜索中... 已搜索: {self.searched_files}/{self.total_files} 文件 ({progress:.1f}%), 找到 {len(self.results)} 个结果"
            
            # 在主线程中更新UI
            self.root.after(0, lambda: self._update_ui(
                status_text, 
                progress, 
                f"{self.searched_files} 个文件",
                f"{len(self.results)} 个结果",
                f"{speed:.1f} 文件/秒",
                formatted_elapsed,
                estimated_time
            ))
    
    def _update_ui(self, status_text, progress, searched_text, found_text, speed_text, elapsed_text, estimated_text):
        """实际更新UI的方法"""
        with self.ui_lock:
            self.status_bar.config(text=status_text)
            self.progress_var.set(progress)
            self.searched_files_var.set(searched_text)
            self.found_files_var.set(found_text)
            self.search_speed_var.set(speed_text)
            self.elapsed_time_var.set(elapsed_text)
            self.estimated_time_var.set(estimated_text)
    
    def search_complete(self):
        """搜索完成后的处理"""
        self.search_in_progress = False
        
        # 计算总时间
        total_time = (datetime.now() - self.search_start_time).total_seconds()
        hours = int(total_time // 3600)
        minutes = int((total_time % 3600) // 60)
        seconds = int(total_time % 60)
        formatted_total = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
        
        # 更新状态栏
        if self.stop_event.is_set():
            status_text = f"搜索已取消! 共搜索 {self.searched_files} 个文件，找到 {len(self.results)} 个结果，耗时: {formatted_total}"
            self.search_status_var.set("已取消")
        else:
            status_text = f"搜索完成! 共搜索 {self.searched_files} 个文件，找到 {len(self.results)} 个结果，耗时: {formatted_total}"
            self.search_status_var.set("已完成")
        
        # 更新进度条为100%（如果搜索完成）
        if not self.stop_event.is_set() and self.total_files > 0:
            self.progress_var.set(100)
        
        # 更新已用时间
        self.elapsed_time_var.set(formatted_total)
        self.estimated_time_var.set("00:00:00")
        
        # 在主线程中更新UI
        self.status_bar.config(text=status_text)
        
        # 更新按钮状态
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
    
    def open_file(self, event=None):
        """打开文件"""
        try:
            selected_items = self.result_tree.selection()
            if not selected_items:
                return
                
            for item in selected_items:
                file_path = self.result_tree.item(item, "values")[0]
                
                if not os.path.exists(file_path):
                    messagebox.showwarning("警告", f"文件不存在: {file_path}")
                    continue
                
                os.startfile(file_path)
        except Exception as e:
            messagebox.showerror("错误", f"无法打开文件: {str(e)}")
    
    def open_file_location(self):
        """打开文件所在位置"""
        try:
            selected_items = self.result_tree.selection()
            if not selected_items:
                return
                
            for item in selected_items:
                file_path = self.result_tree.item(item, "values")[0]
                
                if not os.path.exists(file_path):
                    messagebox.showwarning("警告", f"文件不存在: {file_path}")
                    continue
                
                directory = os.path.dirname(file_path)
                os.startfile(directory)
        except Exception as e:
            messagebox.showerror("错误", f"无法打开文件位置: {str(e)}")
    
    def delete_file(self):
        """删除文件"""
        try:
            selected_items = self.result_tree.selection()
            if not selected_items:
                return
                
            count = len(selected_items)
            if count == 1:
                file_path = self.result_tree.item(selected_items[0], "values")[0]
                file_name = os.path.basename(file_path)
                if not messagebox.askyesno("确认删除", f"确定要删除文件 '{file_name}' 吗?\n此操作不可恢复!"):
                    return
            else:
                if not messagebox.askyesno("确认删除", f"确定要删除选中的 {count} 个文件吗?\n此操作不可恢复!"):
                    return
                
            for item in selected_items:
                file_path = self.result_tree.item(item, "values")[0]
                
                if not os.path.exists(file_path):
                    messagebox.showwarning("警告", f"文件不存在: {file_path}")
                    continue
                
                try:
                    os.remove(file_path)
                    self.result_tree.delete(item)
                except Exception as e:
                    messagebox.showerror("错误", f"无法删除文件 '{os.path.basename(file_path)}': {str(e)}")
            
            messagebox.showinfo("操作完成", f"已成功删除 {count} 个文件")
        except Exception as e:
            messagebox.showerror("错误", f"删除文件时出错: {str(e)}")
    
    def copy_file_path(self):
        """复制文件路径到剪贴板"""
        try:
            selected_items = self.result_tree.selection()
            if not selected_items:
                return
                
            paths = [self.result_tree.item(item, "values")[0] for item in selected_items]
            
            self.root.clipboard_clear()
            self.root.clipboard_append("\n".join(paths))
            
            messagebox.showinfo("操作完成", f"已复制 {len(paths)} 个文件路径到剪贴板")
        except Exception as e:
            messagebox.showerror("错误", f"复制文件路径时出错: {str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = FileSearchTool(root)
    root.mainloop()

    
    
    




